#include "SudokuChromosome.h"
#include "parameters.h"
#include "math.h"

//TODO should not be used
SudokuChromosome::SudokuChromosome() : IntegerChromosome()
{
    TRACE_FUNC
}

SudokuChromosome::SudokuChromosome(int size, QList<QList<int> > * _cluesArr, QList<int> _cellIndexList1, int _sudokuDiagramSize) :  IntegerChromosome(size)
{
    TRACE_FUNC
    cluesArr = _cluesArr;
    cellIndexList = QList<int>(_cellIndexList1);
    //if(cellIndexList.size() == 0)
    //    qFatal("SudokuChromosome::SudokuChromosome empty cellIndexList");
    //qDebug () << "SudokuChromosome::SudokuChromosome" << cellIndexList.size();
    sudokuDiagramSize = _sudokuDiagramSize;
    sudokuDiagramSizeBox = sqrt(sudokuDiagramSize);
}

SudokuChromosome::SudokuChromosome(const SudokuChromosome &other) : IntegerChromosome(other)
{
    TRACE_FUNC
    cluesArr = other.cluesArr;
    cellIndexList = QList<int>(other.cellIndexList);//TODO it is slow
    sudokuDiagramSize = other.sudokuDiagramSize;
    sudokuDiagramSizeBox = other.sudokuDiagramSizeBox;
}

SudokuChromosome::~SudokuChromosome()
{
    TRACE_FUNC
}

SudokuChromosome& SudokuChromosome::operator=(const SudokuChromosome& other)
{
    this->IntegerChromosome::operator=(other);

    cluesArr = other.cluesArr;
    cellIndexList = other.cellIndexList;
    sudokuDiagramSize = other.sudokuDiagramSize;
    sudokuDiagramSizeBox = other.sudokuDiagramSizeBox;

    return *this;
}

void SudokuChromosome::setRandom()
{
    TRACE_FUNC_ENTRY
    if(cluesArr->size() != geneArray.size())
        qFatal("wrong clues array");

    for(int i=0;i<cluesArr->size();++i)
    {
        geneArray.replace(i, getRandomValue(i));
    }
    TRACE_FUNC_EXIT
}

void SudokuChromosome::mutate(int index)
{
    TRACE_FUNC_ENTRY
    //qDebug() << "mutationPosition: " << index + 1;
    //qDebug() << "before mutation: " << *this;
    //int count = 0;
    int newValue;

    if(cluesArr->at(index).size() == 1)
        qFatal("Mutation - only one possible value");

    while( (newValue = getRandomValue(index)) == geneArray.at(index) )
    {
        //qDebug() << "MUTATION LOOP rand val: " << newValue << "old: " << geneArray.at(index) << "count: " << count;
        //count++;
    }
    geneArray.replace(index, newValue);
    //qDebug() << "after  mutation: " << *this;
    TRACE_FUNC_EXIT
}

void SudokuChromosome::additionalOperators(int position)
{
    //int position = rand() % size();

    QList<int> list = createPossiblMatchCellsList(position);

    if(list.isEmpty())
        return;

    //int randomNumberIndex = qrand() % cluesArr->at(position).size();
    //int randomNumber = geneArray.at(position);//cluesArr->at(position).at(randomNumberIndex);
    int position2 = list.at(rand() % list.size());


    //qDebug() << "position: " << position << "ind: " << cellIndexList.at(position);
    //qDebug() << " position2: " << position2 << "ind: " << cellIndexList.at(position2);
    //qDebug() << "position val: " << geneArray.at(position);
    //for(int i=0;i<cluesArr->at(position).size();++i)
    //    qDebug() << cluesArr->at(position).at(i);

    //qDebug() << "position2 val: " << geneArray.at(position2);
    //for(int i=0;i<cluesArr->at(position2).size();++i)
    //    qDebug() << cluesArr->at(position2).at(i);

    if(geneArray.at(position) == geneArray.at(position2))
        mutate(position);
    else
    {
        int tmp = geneArray.at(position);
        //qDebug() << "1position val: " << geneArray.at(position)
        //        << "position2 val: " << geneArray.at(position2);
        geneArray.replace(position,geneArray.at(position2));
        //qDebug() << "2position val: " << geneArray.at(position)
        //        << "position2 val: " << geneArray.at(position2);
        //BUG !!!!!!!!!!!!!!!!!!!!
        geneArray.replace(position2,tmp);//geneArray.at(position));
        //qDebug() << "3position val: " << geneArray.at(position)
        //        << "position2 val: " << geneArray.at(position2);
    }
}

QList<int> SudokuChromosome::createPossiblMatchCellsList(int index)
{
    //qDebug() << "SudokuChromosome::createPossiblMatchCellsList >" << index;
    int value = geneArray.at(index);
    //qDebug() << "value" << value;
    QList<int> matchList;

    if(cellIndexList.size() == 0)
        qFatal("SudokuChromosome::createPossiblMatchCellsList empty cellIndexList");
    //qDebug() << "cellIndexList size: " << cellIndexList.size();

    //qDebug() << "cell # +1: " << cellIndexList.at(index)+1;
    //qDebug()  << "sudokuDiagramSize" << sudokuDiagramSize;

    int row = (cellIndexList.at(index)+1)/(sudokuDiagramSize);
    int col = (cellIndexList.at(index)+1)%(sudokuDiagramSize);

    //qDebug() << "row: " << row << " col: " << col;

    int begBoxX = (row-1) - (row-1) % sudokuDiagramSizeBox;
    int begBoxY = (col-1) - (col-1) % sudokuDiagramSizeBox;

    for(int i=0;i<cellIndexList.size();++i)
    {
        if(i != index)
        {
            int cellRow = (cellIndexList.at(i)+1)/(sudokuDiagramSize);
            int cellCol = (cellIndexList.at(i)+1)%(sudokuDiagramSize);

            int cellBegBoxX = (cellRow-1) - (cellRow-1) % sudokuDiagramSizeBox;
            int cellBegBoxY = (cellCol-1) - (cellCol-1) % sudokuDiagramSizeBox;

            //if cell is in the same row, col or box
            if(row == cellRow ||
               col == cellCol ||
               (begBoxX == cellBegBoxX && begBoxY == cellBegBoxY) )
            {
                //value can be inserted in found cell
                if(cluesArr->at(i).contains(value))
                {
                    int foundCellValue = geneArray.at(i);
                    //if value from found cell can be inserted in current cell
                    if(cluesArr->at(index).contains(foundCellValue))
                        matchList.append(i);
                }
            }
        }
    }

    //qDebug() << "found match cells count: " << matchList.size();
    //for(int j=0;j<matchList.size();++j)
    //    qDebug() << matchList.at(j);

    return matchList;
}


/*void SudokuChromosome::additionalOperators()
{
    int position = rand() % size();
    int randomNumberIndex = qrand() % cluesArr->at(position).size();
    int randomNumber = geneArray.at(position);//cluesArr->at(position).at(randomNumberIndex);
    int position2 = 0;

    do
    {
        position2 = rand() % size();
    }
    while( !(cluesArr->at(position2).contains(randomNumber) ) ||
           !(cluesArr->at(position).contains(geneArray.at(position2))));

    //qDebug() << "position: " << position << " position2: " << position2;
    //qDebug() << "position val: " << geneArray.at(position);
    //for(int i=0;i<cluesArr->at(position).size();++i)
    //    qDebug() << cluesArr->at(position).at(i);

    //qDebug() << "position2 val: " << geneArray.at(position2);
    //for(int i=0;i<cluesArr->at(position2).size();++i)
    //    qDebug() << cluesArr->at(position2).at(i);

    if(geneArray.at(position) == geneArray.at(position2))
        mutate(position);
    else
    {
        int tmp = geneArray.at(position);
        geneArray.replace(position,geneArray.at(position2));
        geneArray.replace(position2,geneArray.at(position));
    }
}*/

int SudokuChromosome::getRandomValue(int index)
{
    //TRACE_FUNC
    int randomIndex = qrand() % cluesArr->at(index).size();
    return cluesArr->at(index).at(randomIndex);
}

Chromosome* SudokuChromosome::duplicate() const
{
    TRACE_FUNC
    return new SudokuChromosome(*this);
}
